Implementation notes for the common library for !ResEd
======================================================

Contents
--------

1  Input focus and setting the caret
2  Registration
  2.1  Registering windows
  2.2  Registering menus
3  Responding to HelpRequest messages
  3.1  Basic processing
  3.2  Substitution
  3.3  !Help abbreviations
  3.4  Multi-icon widgets
4  Focus


1  Input focus and setting the caret
------------------------------------

The dbox module provides two functions to place the caret in a dialogue box;
these behave as follows:

  dbox_set_caret_to (w, i)

    Ensures that the given window has input focus.

    If w does not already have the caret, it is placed in icon i.
    A check is now made to see that the icon containing the caret is not
     faded; if it is, the caret is moved to the next unfaded icon (if any).

  dbox_place_caret (w, i)

    Ensures that the given window has input focus.

    Places the caret inside the icon i in window w.


The first function is called to place the caret into a dialogue box - for
example, when the user clicks somewhere inside that dialogue box.

The second function is used to place the caret into a specific icon - for
example, when the user has switched on an option button and the caret is to
be placed in an associated writable.


2  Registration
---------------

2.1  Registering windows
------------------------

Whenever a (wimp) window is created, it is "registered" by calling the
function registry_register_window(..) passing the following parameters:

  handle      - the window's handle
  type        - a value identifying the kind of window it is; see h.main in
                 the shell, !Window, !Menu and !Misc directories for the
                 possible values
  closure     - a value of use to the application (eg a WindowObjPtr for
                 an editing window)

A corresponding function - registry_deregister_window(..) - is called when
the window is deleted.

Whenever the application needs to find out about a particular window (for
example when a mouse-click occurs, or a HelpRequest message arrives), the
function registry_lookup_window(..) is called: a window handle is passed in,
and the window's type and closure are returned as result.

Note that it is not just editing windows and dialogue boxes that must be
registered: any window which is required to respond to interactive help must
be registered, since the response is determined according to the type of the
window.


2.2  Registering menus
----------------------

A similar scheme is used to register menus, although this is simpler in two
respects: no closure is recorded, and a simple linear array is held with
space for a maximum of 20 entries - there are not many menus per application.
The functions are:

  menu_register (menu, type)
  menu_deregister (menu)
  type = menu_lookup_type()   /* returns type of current menu */

Menus are registered only to enable the correct interactive help to be
supplied: once again, the response is determined by the type of the menu.


3 Responding to HelpRequest messages
------------------------------------

Each of the four components of ResEd has its own customised c.help module,
but in a future version this code should be generalised and moved to
commonlib.


3.1 Basic processing
--------------------

The basic idea is as follows:

  Determine the context for the help message by discovering where the mouse
  pointer is:

    - over the application icon
    - over an editing window
    - over a document window
    - over the palette
    - over a properties dialogue box
    - over a menu

  Construct a token that corresponds to this context*; for example, if the
  pointer is over the option icon labelled "Deliver events - Value changed"
  in the String Set gadget properties dialogue box, the token would be
  "Hlp.G.StringSet.DELIVERVALUE".

  This token is then looked up in the Messages file. If it is not present,
  then some simpler token derived from it is looked up - and so on, according
  to a set of rules appropriate to the context*. Carrying on with the
  example, the following tokens would be looked up in order until one was
  found:

    Hlp.G.StringSet.DELIVERVALUE
    Hlp.G.DELIVERVALUE
    Hlp.DELIVERVALUE
    Hlp.G.StringSet
    Hlp.G             [this token is always present]

  The text is then scanned - repeatedly if necessary - for substitutions: a
  substitution is introduced by ! and may have parameters. Each substitution
  involves looking up another token in the message file - details are given
  below. In our example, the following entries are found in the Messages
  file:

    Hlp.G.StringSet.DELIVERVALUE:!SWVC/StringSet_ValueChanged;
    !SWVC:!SWD/%s events when the user changes the value of this gadget;
    !SWD:Switch on to receive %s.

  which result in the following substitutions:

    !SWVC/StringSet_ValueChanged;
    !SWD/StringSet_ValueChanged events when the user changes the value of
        this gadget;
    Switch on to receive StringSet_ValueChanged events when the user changes
        the value of this gadget.

  Finally, the resultant string is sent back to !Help as the text of a
  HelpReply message.

    [* The way in which the token is constructed from the context, and the
       rules for looking up "related" tokens where necessary, are specific
       to each ResEd component - see the appropriate doc file. ]


3.2 Substitution
----------------

A "macro-call" has the form:

    !<id> [/param ...];

A maximum of three parameters is allowed; example calls are:

    !MC0;
    !MC2/letters/digits;

The function help_expand(..) is called to replace the macro-call by its
substitution, which is defined as the contents of the string res where:

    text = message_lookup("!<id>");
    sprintf(res, text, param1, param2, param3);

Finally, help_expand(..) is called repeatedly until there are no more macro
calls to replace.

  [ Note that an exclamation mark *can* be included in a help string provided
    that it is not recognised as part of a macro call: if help_expand() is
    unable to locate a token !<id> in the Messages file, then the text is
    unaltered. ]


3.3 !Help abbreviations
-----------------------

Use is also made of "standard" abbreviations which are built in to !Help.
Any two-character sequence of the form "\<char>" where <char> is in the
table below is replaced by the corresponding string:

  S   Click SELECT to
  R   Move the pointer right to
  A   Click ADJUST to
  T   This is the
  G   This option is greyed out because
  W   This window is
  D   Drag SELECT to
  d   Drag ADJUST to
  w   window
  s   SELECT
  a   ADJUST

For example, the string:

     \Sdeselect all gadgets.

will be displayed by !Help as:

     Click SELECT to deselect all gadgets.


3.4 Multi-icon widgets
----------------------

!WinEdit allows you to "name" icons by including a special string in the
icon's validation field, and this feature is used by !ResEd in two ways.

Firstly, !WinEdit is used to create a header file h.icondefs which includes
#defines for every named icon in the Templates file; for example, the "Faded"
option icon in the RadioButt template (ie the template for the radio button
gadget properties dialogue box) is named "FADED", and the h.icondefs file
contains the entry:

     #define I_RADIOBUTT_FADED 8

This makes the ResEd source files independent of icon numbers, whilst not
imposing any run-time overhead. Note also that h.icondefs is automatically
re-generated by the MakeFile if the Templates file is edited, and so any
source recompilations will happen as required whenever icons are renumbered.

Secondly, the names of icons are used by the help processing system in order
to construct an appropriate token to look up in the Messages file.

To do this, the function wimp_read_icon_name(..) is called. This function
searches for a string of the form:

     N<type>/<name>;

inside the icon's validation string, and then returns the <name> part of the
string.

This is fine for most of the "widgets" supported by !WinEdit, since they are
composed of just a single icon; but there are two important exceptions which
must be handled specially. These are the labelled box (which has two icons -
the label and the frame), and the adjuster (which has two icons - an up and a
down arrow).

In these cases, the widget is named as a whole by !WinEdit, which then
constructs derived names for the header file. For example, suppose you create
a labelled box named BOX and an adjuster named ADJ in a template called Tmpl;
then the header file will contain four entries - one for each *icon* - as
follows:

     #define I_TMPL_BOX_FRAME 10
     #define I_TMPL_BOX_LABEL 11

     #define I_TMPL_ADJ_DOWN 22
     #define I_TMPL_ADJ_UP 23

Each of these "widgets" consists of a "master" icon (the FRAME or DOWN one)
and a "slave" icon (the LABEL or UP one). !WinEdit only stores the widget's
name in the master icon, in the form:

     N<type>/<name>/<ref>;

where <ref> is the number of the slave icon; in its turn, the slave icon
includes a reference to its master:

     N/<ref>;

For example, the two icons for the adjuster above would include the following
strings in their validation fields:

    icon 22 (master):    N11/ADJ/23;       [11 is the adjuster widget type]
    icon 23 (slave):     N/22;

wimp_read_icon(..) recognises slave icons, and follows the reference back to
the master icon; it also recognises the two widget types "labelled box" and
"adjuster", and generates appropriately extended names for each of the
component icons in such widgets.


4  Focus
--------

The module c.focus implements "focus" as in the new "Drag-n-drop" protocol.

This is not exactly the same as "input focus" in the Wimp sense; only
"document windows" - eg the shell windows, menu edit windows and window edit
windows - may have "focus".

But of course any dialogue box may have the caret (and hence a yellow title
bar and "input focus"); this makes no difference to which document window
retains the "focus".

However, when a dialogue box is closed, focus is always returned to its
parent document window - together with the caret and input focus as well.


The relevant functions are:

  focus_claim (int window, FocusCallBack cb, void *closure)

    If the window already has focus, no further action is necessary.

    If focus is currently with another window belonging to this task, and
    there is an associated callback function, call it*:
        cb(FocusLost, window, closure);

    If focus is not with this task at all, broadcast Message_ClaimEntity to
    claim the caret and selection.

    If a callback function was supplied, call it:
        cb(FocusGained, window, closure);

      * This callback function will also be called if focus is lost to
        another task (ie upon receipt of a Message_ClaimEntity message).

  focus_giveup (int window)

    Simply notes that no window in this task has focus (because the window
    with focus has been deleted, for example).

  window = focus_current ()

    Returns the handle of the window with focus (or -1 if none).
